es6块级作用域踩坑

Author Avatar
cowboy 8月 04, 2017
  • 在其它设备中阅读本文章

本来以为自己对js作用域这方面已经很了解了,但是看到这样一个简单的问题后,通过查阅却发现这里面其实还有不小的坑。

foo();
if (true) {
  function foo() {
    console.log('1111');
  }
} else {
    function foo() {
      console.log('2222');
  }
}

初看好像没什么难度,由于js函数声明提升,,后面声明的foo覆盖第一个,最后打印2222。
但实际结果却是这样的

经过查阅资料和博客,发现这个问题其实在ES5和ES6中有着天壤之别,并且浏览器的处理方式和语法规定也不相同。
在ES5中是这样规定的

函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明

但是es5并没有块级作用域,同时浏览器为了兼容,还是还是支持在块级作用域之中声明函数
如果是这样的话不就是最开始分析的那样了吗,实际上自己在测试的时候使用的是chrome浏览器,chrome是支持es6的,es6对块级作用域内的函数声明处理却是这样的

ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。

按照这种规定,理论上输出的也是1111,但为什么是报错
原来如果改变了块级作用域内声明的函数的处理规则,显然会对老代码产生很大影响。为了减轻因此产生的不兼容问题,ES6在附录B里面规定,浏览器的实现可以不遵守上面的规定,有自己的行为方式。

-允许在块级作用域内声明函数。
-函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
-同时,函数声明还会提升到所在的块级作用域的头部。

所以在浏览器的 ES6 环境中,块级作用域内声明的函数,行为类似于var声明的变量
由于变量提升,实际代码应该是这样的

var foo = undefined
foo();
if (true) {
  function foo() {
    console.log('1111');
  }
} else {
    function foo() {
      console.log('22222');
  }
}

考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。

参考:阮一峰《ECMAScript6入门》